package dao;

import entities.News;

import javax.ejb.Stateless;
import javax.persistence.Query;
import java.util.ArrayList;
import java.util.List;

/**
 * News DAO layer
 * User: kaiser
 * Date: 7/31/13
 * Time: 9:15 PM
 */
@Stateless
public class NewsDAO extends DAO<News> {

    @Override
    public List<News> getAll() {
        return getEntityManager().createQuery("SELECT c FROM Channel c").getResultList();
    }

    /**
     * @param channel channel primary name
     * @param limit   maximum list size
     * @return List wit channels or null if no one exist
     */
    public List<News> getNewsByChannel(String channel, int limit) {
        Query query = getEntityManager().createQuery(
                "SELECT n FROM News n " +
                        "WHERE n.channel.id = :channel " +
                        "ORDER BY n.rank DESC, n.published DESC ");

        query.setParameter("channel", channel);
        query.setMaxResults(limit);
        return query.getResultList();
    }

    /**
     * Get News list sorted by raiting and publish date
     *
     * @param limit max news count in list
     * @return List of news if exist otherwise null
     */
    public List<News> getTopNews(int limit) {
        Query query = getEntityManager()
                .createQuery("SELECT n FROM News n ORDER BY n.rank DESC, n.published DESC ")
                .setMaxResults(limit);

        return query.getResultList();
    }

    /**
     * Check by id is news exist DB
     *
     * @param id of news
     * @return true if exist
     */
    public boolean isExist(int id) {
        return getById(id) != null;
    }

    /**
     * Check is entity News exist by title, channel, url and date criteria
     *
     * @param news entity with title, channel, url and date criteria
     * @return true if exist
     */
    public boolean isExist(News news) {
        Query query = getEntityManager()
                .createQuery("SELECT COUNT(n) FROM News n WHERE " +
                        "n.title =:title " +
                        "AND n.url = :url " +
                        "AND n.channel.id =:channel " +
                        "AND n.published = :published"
                );

        query.setParameter("title", news.getTitle())
                .setParameter("url", news.getUrl())
                .setParameter("channel", news.getChannel().getId())
                .setParameter("published", news.getPublishedDate());

        ;
        long count = (long) query.getSingleResult();
        return count > 0;
    }

    /**
     * Get News entity by Id
     *
     * @param id of needed news
     * @return News entity if exist otherwise null
     */
    public News getById(int id) {
        return getEntityManager().find(News.class, id);
    }

    /**
     * increase news rate on one
     *
     * @param id news id for increase rate
     */
    public void upNewsRank(int id) {
        getEntityManager()
                .createQuery("UPDATE News SET rank = rank+1 WHERE id=:newsId")
                .setParameter("newsId", id)
                .executeUpdate();
    }

    /**
     * Remove not popular old chanel news.
     *
     * @param channel             channel primary name old news which will be removed
     * @param inviolableNewsCount count of news to left
     */
    public void removeOldNotPopularNews(String channel, int inviolableNewsCount) {
        /*
         * JPQL doesn't support subqueries in order by.
         */
        List<News> popular = getNewsByChannel(channel, inviolableNewsCount);
        List<Integer> popularId = new ArrayList<>();

        for (News news : popular) {
            popularId.add(news.getId());
        }

        getEntityManager()
                .createQuery("DELETE FROM News n " +
                        "WHERE n.channel.id=:channel " +
                        "AND n.id NOT IN :popular")
                .setParameter("popular", popularId)
                .setParameter("channel", channel)
                .executeUpdate();
    }
}
